﻿using CTMJobTransferData.Models;
using log4net;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Entity.SqlServer;
using System.Data.Entity.Validation;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CTMJobTransferData
{
    class Program
    {
        static void Main(string[] args)
        {
            var isSendMailToSupporter = Convert.ToBoolean(ConfigurationManager.AppSettings["SendMailToSupporterOnApplicationError"]);

            try
            {
                // Create new stopwatch.
                var stopwatch = new Stopwatch();

                // Begin timing.
                stopwatch.Start();

                using (var ctxCTM = new CTMEntities())
                {
                    ctxCTM.Database.CommandTimeout = 360;

                    var types = ctxCTM.OrgTypes;
                    var typeLegalEntity = types.FirstOrDefault(o => o.Name == "Legal Entity");
                    var typeDivision = types.FirstOrDefault(o => o.Name == "Division");
                    var typeDepartment = types.FirstOrDefault(o => o.Name == "Department");
                    var typeGroup = types.FirstOrDefault(o => o.Name == "Group");
                    if (typeLegalEntity != null && typeDivision != null && typeDepartment != null && typeGroup != null)
                    {
                        using (var ctxGen = new GENDBEntities())
                        {
                            ctxGen.Database.CommandTimeout = 360;
                            Process(ctxCTM, ctxGen, typeLegalEntity, typeDivision, typeDepartment, typeGroup);
                        }
                    }

                    // execute stored procedure
                    ctxCTM.CTM_TransferDataSP();
                }

                // Stop timing.
                stopwatch.Stop();

                // Write result.
                Console.WriteLine("Time elapsed: {0:hh\\:mm\\:ss}", stopwatch.Elapsed);
            }
            catch (Exception e)
            {
                //var message = e.ToMessageAndCompleteStacktrace();
                var message = e.GetAllExceptionInfo();

                LogManager.GetLogger("ErrorLogger").Error(message);
                if (isSendMailToSupporter)
                {
                    LogManager.GetLogger("EmailLogger").Error(message);
                }
            }
            LogManager.GetLogger("ErrorLogger").Info("Finish transfer data job!");
        }

        private static List<TempOrg> CreateOrgList(CTMEntities ctxCTM, GENDBEntities ctxGen, List<Org> dbOrgs, OrgType typeLegalEntity, OrgType typeDivision, OrgType typeDepartment, OrgType typeGroup)
        {
            var orgs = new List<TempOrg>();

            var ddg = ctxGen.v_DivDeptGrp.AsNoTracking().Select(x => new TempDivDeptGrp
            {
                OrgCode = x.SRG_cOrgCode.Trim(),
                DivisionCode = x.SRG_cDivisionCode.Trim(),
                DeptCode = x.SRG_cDeptCode.Trim(),
                GroupCode = x.SRG_cGroupCode.Trim(),
                OrgHeadEnum = x.SRG_iOrgHeadEnum,
                DivHeadEnum = x.SRG_iDivHeadEnum,
                DeptHeadEnum = x.SRG_iDeptHeadEnum,
                GroupHeadEnum = x.SRG_iGroupHeadEnum,
                IsActive = x.Active
                //ParentDeptCode = x.SRG_cParentDeptCode
            });

            foreach (var item in ddg)
            {
                OrgType type = null;
                var name = string.Empty;
                var managerId = string.Empty;
                var parentId = string.Empty;    // cheat
                if (item.OrgCode == item.DivisionCode && item.DivisionCode == item.DeptCode && item.DeptCode == item.GroupCode)
                {
                    type = typeLegalEntity;
                    name = item.OrgCode;
                    managerId = item.OrgHeadEnum.Value.ToString();
                    //parentId = item.OrgHeadEnum.Value.ToString();   // itself
                }
                else
                {
                    if (item.DivisionCode == item.DeptCode && item.DeptCode == item.GroupCode)
                    {
                        type = typeDivision;
                        name = item.DivisionCode;
                        managerId = item.DivHeadEnum.Value.ToString();
                        //parentId = item.OrgHeadEnum.Value.ToString();
                        parentId = item.OrgCode;
                    }
                    else
                    {
                        if (item.DeptCode == item.GroupCode)
                        {
                            type = typeDepartment;
                            name = item.DeptCode;
                            managerId = item.DeptHeadEnum.Value.ToString();
                            //parentId = item.DivHeadEnum.Value.ToString();
                            parentId = item.DivisionCode;
                        }
                        else
                        {
                            type = typeGroup;
                            name = item.GroupCode;
                            managerId = item.GroupHeadEnum.Value.ToString();
                            //parentId = item.DeptHeadEnum.Value.ToString();
                            parentId = item.DeptCode;
                        }
                    }
                }

                var id = Guid.NewGuid();

                // check if exist
                var dbOrg = dbOrgs.FirstOrDefault(o => o.Name == name);
                if (dbOrg != null) id = dbOrg.Id;

                var org = new TempOrg
                {
                    Id = id,
                    Name = name,
                    Description = name,
                    OrgTypeId = type.Id,
                    IsDeleted = item.IsActive.HasValue ? !item.IsActive.Value : false,
                    //CreatedOn = DateTime.UtcNow,
                    //TransferredOn = DateTime.UtcNow,
                    ManagerId = managerId,
                    //ParentId = string.IsNullOrEmpty(parentId) ? null : parentId,
                    ParentId = parentId,
                    //Path = string.Empty + id,
                    //Path = string.IsNullOrEmpty(parentId) ? name : parentId + "/" + name
                };
                orgs.Add(org);
            }
            return orgs;
        }

        private static void Process(CTMEntities ctxCTM, GENDBEntities ctxGen, OrgType typeLegalEntity, OrgType typeDivision, OrgType typeDepartment, OrgType typeGroup)
        {
            var dbOrgs = ctxCTM.Orgs.AsNoTracking().ToList();
            var dbEmps = ctxCTM.AspNetUsers.AsNoTracking().ToList();

            var dbDomains = (from e in dbEmps
                             where !(e.NTID == null || e.NTID.Equals(""))
                             select e.NTID).ToList();

            var tmpOrgs = CreateOrgList(ctxCTM, ctxGen, dbOrgs, typeLegalEntity, typeDivision, typeDepartment, typeGroup);

            string password = "ctm@2015";
            var passwordHasher = new PasswordHasher();
            var hashedPassword = passwordHasher.HashPassword(password);
            var stamps = Guid.NewGuid().ToString().ToLower();

            var distinctEmps = from e in ctxGen.Employees.AsNoTracking()
                               where !(e.DomainID == null || e.DomainID.Equals(""))
                               group e by e.DomainID into grp
                               select grp.OrderByDescending(g => g.CreatedDate).FirstOrDefault();

            var emps = distinctEmps.Select(e => new TempEmployee
            {
                Enum = e.@enum.ToString(),
                Name = e.name.Trim(),
                GroupCode = string.IsNullOrEmpty(e.GroupCode) ? string.Empty : e.GroupCode.Trim(),
                Dept = string.IsNullOrEmpty(e.DEPT) ? string.Empty : e.DEPT.Trim(),
                Email = string.IsNullOrEmpty(e.email) ? string.Empty : e.email.Trim(),
                LastModifiedDate = e.LastModifiedDate,
                MgrEnumber = e.MgrEnumber.HasValue ? e.MgrEnumber.ToString() : string.Empty,
                DomainId = e.DomainID.Trim(),
                Id = Guid.NewGuid().ToString().ToLower()
            }).ToList();

            //AspNetUser
            var users = emps.Select(emp => new TempUser
            {
                Id = dbDomains.Contains(emp.DomainId) ? (from e1 in dbEmps where e1.NTID == emp.DomainId select e1.Id).FirstOrDefault() : emp.Id,
                Avatar = string.IsNullOrEmpty(emp.Email) ? string.Empty : string.Format("https://rb-owa.apac.bosch.com/ews/exchange.asmx/s/GetUserPhoto?email={0}&size=HR240x240", emp.Email),
                DisplayName = emp.Name,
                NTID = emp.DomainId,
                Email = emp.Email,
                PasswordHash = hashedPassword,
                SecurityStamp = stamps,
                AccessFailedCount = 0,
                UserName = emp.DomainId,
                LockoutEnabled = false,
                TransferredOn = DateTime.UtcNow,
                EmpNo = emp.Enum,
                ManagerId = dbDomains.Contains(emp.DomainId) ? (from e1 in dbEmps where e1.NTID == emp.DomainId select e1.ManagerId).FirstOrDefault() : (from e in emps where e.Enum == emp.MgrEnumber select e.Id).FirstOrDefault(),
                OrgId = (from o in tmpOrgs where o.Name == emp.GroupCode select o.Id).FirstOrDefault()
            });

            //Org
            var orgs = new List<Models.TempOrg>();

            var q = (from o in tmpOrgs.Where(o => o.OrgTypeId == typeLegalEntity.Id)
                     join emp in users on o.ManagerId equals emp.EmpNo into e
                     from u in e.DefaultIfEmpty()
                     select new Models.TempOrg
                     {
                         Id = o.Id,
                         Name = o.Name,
                         Description = o.Description,
                         OrgTypeId = o.OrgTypeId,
                         IsDeleted = o.IsDeleted,
                         CreatedOn = DateTime.UtcNow,
                         TransferredOn = DateTime.UtcNow,
                         ManagerId = u == null ? null : u.Id,
                         Path = string.Empty + o.Id
                     });

            orgs.AddRange(q);

            q = (from o in tmpOrgs.Where(o => o.OrgTypeId == typeDivision.Id)
                 join emp in users on o.ManagerId equals emp.EmpNo into e
                 from u in e.DefaultIfEmpty()
                 join tmpOrg in tmpOrgs on o.ParentId equals tmpOrg.Name into tmpOrg1
                 from o1 in tmpOrg1.DefaultIfEmpty()
                 join org in orgs on o.ParentId equals org.Name into org1
                 from o2 in org1.DefaultIfEmpty()
                 select new Models.TempOrg
                {
                    Id = o.Id,
                    Name = o.Name,
                    Description = o.Description,
                    OrgTypeId = o.OrgTypeId,
                    IsDeleted = o.IsDeleted,
                    CreatedOn = DateTime.UtcNow,
                    TransferredOn = DateTime.UtcNow,
                    ManagerId = u == null ? null : u.Id,
                    ParentId = o1 == null ? Guid.Empty : o1.Id,
                    Path = (o2 == null ? string.Empty + o.Id : o2.Path + "/" + o.Id)
                });

            orgs.AddRange(q);

            q = (from o in tmpOrgs.Where(o => o.OrgTypeId == typeDepartment.Id)
                 join emp in users on o.ManagerId equals emp.EmpNo into e
                 from u in e.DefaultIfEmpty()
                 join tmpOrg in tmpOrgs on o.ParentId equals tmpOrg.Name into tmpOrg1
                 from o1 in tmpOrg1.DefaultIfEmpty()
                 join org in orgs on o.ParentId equals org.Name into org1
                 from o2 in org1.DefaultIfEmpty()
                 select new Models.TempOrg
                {
                    Id = o.Id,
                    Name = o.Name,
                    Description = o.Description,
                    OrgTypeId = o.OrgTypeId,
                    IsDeleted = o.IsDeleted,
                    CreatedOn = DateTime.UtcNow,
                    TransferredOn = DateTime.UtcNow,
                    ManagerId = u == null ? null : u.Id,
                    ParentId = o1 == null ? Guid.Empty : o1.Id,
                    Path = (o2 == null ? string.Empty + o.Id : o2.Path + "/" + o.Id)
                });

            orgs.AddRange(q);

            q = (from o in tmpOrgs.Where(o => o.OrgTypeId == typeGroup.Id)
                 join emp in users on o.ManagerId equals emp.EmpNo into e
                 from u in e.DefaultIfEmpty()
                 join tmpOrg in tmpOrgs on o.ParentId equals tmpOrg.Name into tmpOrg1
                 from o1 in tmpOrg1.DefaultIfEmpty()
                 join org in orgs on o.ParentId equals org.Name into org1
                 from o2 in org1.DefaultIfEmpty()
                 select new Models.TempOrg
                 {
                     Id = o.Id,
                     Name = o.Name,
                     Description = o.Description,
                     OrgTypeId = o.OrgTypeId,
                     IsDeleted = o.IsDeleted,
                     CreatedOn = DateTime.UtcNow,
                     TransferredOn = DateTime.UtcNow,
                     ManagerId = u == null ? null : u.Id,
                     ParentId = o1 == null ? Guid.Empty : o1.Id,
                     Path = (o2 == null ? string.Empty + o.Id : o2.Path + "/" + o.Id)
                 });

            orgs.AddRange(q);

            //
            ctxCTM.Database.ExecuteSqlCommand("TRUNCATE TABLE [TempOrgs]");
            ctxCTM.TempOrgs.AddRange(orgs);

            ctxCTM.Database.ExecuteSqlCommand("TRUNCATE TABLE [TempUsers]");
            ctxCTM.TempUsers.AddRange(users);
            ctxCTM.SaveChanges();
        }
    }

    public class TempEmployee
    {
        //public int Enum { get; set; }
        public string Enum { get; set; }

        public string Name { get; set; }
        public string GroupCode { get; set; }
        public string Dept { get; set; }
        public string Email { get; set; }
        //public string EnumLevel { get; set; }
        public DateTime? LastModifiedDate { get; set; }

        //public int? MgrEnumber { get; set; }
        public string MgrEnumber { get; set; }

        //public int RvwrEnumber { get; set; }
        public string DomainId { get; set; }

        //public Guid Id { get; set; }
        public string Id { get; set; }
    }

    public class TempDivDeptGrp
    {
        public string OrgCode { get; set; }
        public string DivisionCode { get; set; }
        public string DeptCode { get; set; }
        public string GroupCode { get; set; }
        public int? OrgHeadEnum { get; set; }
        public int? DivHeadEnum { get; set; }
        public int? DeptHeadEnum { get; set; }
        public int? GroupHeadEnum { get; set; }
        //public string ParentDeptCode { get; set; }
        public bool? IsActive { get; set; }
    }

    public class TempOrg
    {
        public Guid Id { get; set; }

        public string ParentId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Path { get; set; }
        public bool IsDeleted { get; set; }
        public DateTime CreatedOn { get; set; }
        public string ManagerId { get; set; }
        public int OrgTypeId { get; set; }
        public Nullable<DateTime> TransferredOn { get; set; }
    }
}
